﻿#region MIT
/*
Engine Of Evermore (http://code.google.com/p/engineofevermore/)
Copyright (c) 2008 Michael Woerister

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
#endregion

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Diagnostics;

namespace EngineOfEvermore.Common
{
    public struct Vector2d
    {
        public float X
        {
            get { return _x; }
            set { _x = value; }
        }

        public float Y
        {
            get { return _y; }
            set { _y = value; }
        }

        public Vector2d( float x, float y )
        {
            _x = x;
            _y = y;
        }

        public Vector2d( Vector2d other )
        {
            _x = other._x;
            _y = other._y;
        }

        public float Length
        {
            get { return (float)Math.Sqrt( LengthSquared ); }
        }

        public float LengthSquared
        {
            get { return _x * _x + _y * _y; }
        }

        public float Normalize()
        {
            if ( _x.EpsilonIsZero() && _y.EpsilonIsZero() )
                return 0.0f;

            float len = Length;
            _x /= len;
            _y /= len;
            return len;
        }

        public void ScaleTo( float length )
        {
            float factor = length / Length;
            _x *= factor;
            _y *= factor;
        }

        public Vector2d GetUnitDirection()
        {
            Vector2d unit = new Vector2d( this );
            unit.Normalize();
            return unit;
        }

        public void ProjectTo( Vector2d dest )
        {
            float len = ( this * dest ) / dest.Length;
            dest.ScaleTo( len );
            this = dest;
        }

        public Vector2d GetProjection( Vector2d dest )
        {
            Vector2d projection = new Vector2d( this );
            projection.ProjectTo( dest );
            return projection;
        }

        public bool EpsilonEquals( Vector2d other, float epsilon )
        {
            return Util.EpsilonEquals( _x, other._x, epsilon ) &&
                   Util.EpsilonEquals( _y, other._y, epsilon );
        }

        public bool EpsilonEquals( Vector2d other )
        {
            return Util.EpsilonEquals( _x, other._x ) &&
                   Util.EpsilonEquals( _y, other._y );
        }

        public void Rotate( float sin, float cos )
        {
            float x = cos * _x - sin * _y;
            _y = sin * _x + cos * _y;
            _x = x;
        }

        public void Rotate( float angleInDegrees )
        {
            double angleInRad = Util.DegToRad( angleInDegrees );
            float sin = (float) Math.Sin( angleInRad );
            float cos = (float) Math.Cos( angleInRad );

            Rotate( sin, cos );
        }

        public static Vector2d operator+( Vector2d v1, Vector2d v2 )
        {
            return new Vector2d( v1._x + v2._x, v1._y + v2._y );
        }

        public static Vector2d operator -( Vector2d v1, Vector2d v2 )
        {
            return new Vector2d( v1._x - v2._x, v1._y - v2._y );
        }

        public static Vector2d operator -( Vector2d v1 )
        {
            return new Vector2d( -v1._x, -v1._y );
        }

        public static float operator *( Vector2d v1, Vector2d v2 )
        {
            return v1._x * v2._x + v1._y * v2._y;
        }

        public static float operator ^( Vector2d v1, Vector2d v2 )
        {
            return v1._x * v2._x - v1._y * v2._y;
        }

        public static Vector2d operator *( Vector2d v, float factor )
        {
            return new Vector2d( v._x * factor, v._y * factor );
        }

        public static Vector2d operator *( float factor, Vector2d v )
        {
            return new Vector2d( v._x * factor, v._y * factor );
        }

        public static Vector2d operator /( Vector2d v, float d )
        {
            return new Vector2d( v._x / d, v._y / d );
        }

        public static bool operator ==( Vector2d v1, Vector2d v2 )
        {
            return ( ( v1._x == v2._x ) && ( v1._y == v2._y ) );
        }

        public static bool operator !=( Vector2d v1, Vector2d v2 )
        {
            return ( ( v1._x != v2._x ) || ( v1._y != v2._y ) );
        }

        public override string ToString()
        {
            return "{ " + _x.ToString() + "; " + _y.ToString() + " }";
        }

        public override int GetHashCode()
        {
            return _x.GetHashCode() ^ _y.GetHashCode();
        }

        public override bool Equals( object obj )
        {
            if ( obj != null && obj is Vector2d )
            {
                return ( (Vector2d) obj ) == this;
            }

            return false;
        }

        public bool Equals( Vector2d other )
        {
            return this == other;
        }

        public static readonly Vector2d Zero = new Vector2d();
        
        float _x;
        float _y;
    }
}
